<template>
  <div class="zjm-editor">
    <el-button @click="clickHandler">点击</el-button>
    <div class="menu" editor-component="menu">
      <div class="menu-item">
        <div class="menu-item__undo">
          <i></i>
        </div>
        <div class="menu-item__redo">
          <i></i>
        </div>
        <div class="menu-item__painter" title="格式刷(双击可连续使用)">
          <i></i>
        </div>
        <div class="menu-item__format" title="清除格式">
          <i></i>
        </div>
      </div>
      <div class="menu-divider"></div>
      <div class="menu-item">
        <div class="menu-item__font">
          <span class="select" title="字体">微软雅黑</span>
          <div class="options">
            <ul>
              <li v-for="item in fontFamilyList" :key="item.value" :data-family="item.value"
                :style="{ fontFamily: item.value }">{{ item.label }}</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__size">
          <span class="select" title="字体">小四</span>
          <div class="options">
            <ul>
              <li v-for="size in fontSizeList" :key="size.value" :data-size="size.value">{{ size.label }}</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__size-add">
          <i></i>
        </div>
        <div class="menu-item__size-minus">
          <i></i>
        </div>
        <div class="menu-item__bold">
          <i></i>
        </div>
        <div class="menu-item__italic">
          <i></i>
        </div>
        <div class="menu-item__underline">
          <i></i>
          <span class="select"></span>
          <div class="options">
            <ul>
              <li data-decoration-style='solid'>
                <i></i>
              </li>
              <li data-decoration-style='double'>
                <i></i>
              </li>
              <li data-decoration-style='dashed'>
                <i></i>
              </li>
              <li data-decoration-style='dotted'>
                <i></i>
              </li>
              <li data-decoration-style='wavy'>
                <i></i>
              </li>
            </ul>
          </div>
        </div>
        <div class="menu-item__strikeout" title="删除线(Ctrl+Shift+X)">
          <i></i>
        </div>
        <div class="menu-item__superscript">
          <i></i>
        </div>
        <div class="menu-item__subscript">
          <i></i>
        </div>
        <div class="menu-item__color" title="字体颜色">
          <i></i>
          <span></span>
          <input type="color" id="color" />
        </div>
        <div class="menu-item__highlight" title="高亮">
          <i></i>
          <span></span>
          <input type="color" id="highlight">
        </div>
      </div>
      <div class="menu-divider"></div>
      <div class="menu-item">
        <div class="menu-item__title">
          <i></i>
          <span class="select" title="切换标题">正文</span>
          <div class="options">
            <ul>
              <li style="font-size:16px;">正文</li>
              <li data-level="first" style="font-size:26px;">标题1</li>
              <li data-level="second" style="font-size:24px;">标题2</li>
              <li data-level="third" style="font-size:22px;">标题3</li>
              <li data-level="fourth" style="font-size:20px;">标题4</li>
              <li data-level="fifth" style="font-size:18px;">标题5</li>
              <li data-level="sixth" style="font-size:16px;">标题6</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__left">
          <i></i>
        </div>
        <div class="menu-item__center">
          <i></i>
        </div>
        <div class="menu-item__right">
          <i></i>
        </div>
        <div class="menu-item__alignment">
          <i></i>
        </div>
        <div class="menu-item__justify">
          <i></i>
        </div>
        <div class="menu-item__row-margin">
          <i title="行间距"></i>
          <div class="options">
            <ul>
              <li data-rowmargin='1'>1</li>
              <li data-rowmargin="1.25">1.25</li>
              <li data-rowmargin="1.5">1.5</li>
              <li data-rowmargin="1.75">1.75</li>
              <li data-rowmargin="2">2</li>
              <li data-rowmargin="2.5">2.5</li>
              <li data-rowmargin="3">3</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__list">
          <i></i>
          <div class="options">
            <ul>
              <li>
                <label>取消列表</label>
              </li>
              <li data-list-type="ol" data-list-style='decimal'>
                <label>有序列表：</label>
                <ol>
                  <li>________</li>
                </ol>
              </li>
              <li data-list-type="ul" data-list-style='checkbox'>
                <label>复选框列表：</label>
                <ul style="list-style-type: '☑️ ';">
                  <li>________</li>
                </ul>
              </li>
              <li data-list-type="ul" data-list-style='disc'>
                <label>实心圆点列表：</label>
                <ul style="list-style-type: disc;">
                  <li>________</li>
                </ul>
              </li>
              <li data-list-type="ul" data-list-style='circle'>
                <label>空心圆点列表：</label>
                <ul style="list-style-type: circle;">
                  <li>________</li>
                </ul>
              </li>
              <li data-list-type="ul" data-list-style='square'>
                <label>空心方块列表：</label>
                <ul style="list-style-type: square;">
                  <li>________</li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </div>
      <div class="menu-divider"></div>
      <div class="menu-item">
        <div class="menu-item__table">
          <i title="表格"></i>
        </div>
        <div class="menu-item__table__collapse">
          <div class="table-close">×</div>
          <div class="table-title">
            <span class="table-select">插入</span>
            <span>表格</span>
          </div>
          <div class="table-panel"></div>
        </div>
        <div class="menu-item__image">
          <i title="图片"></i>
          <input type="file" id="image" accept=".png, .jpg, .jpeg, .svg, .gif">
        </div>
        <div class="menu-item__hyperlink">
          <i title="超链接"></i>
        </div>
        <div class="menu-item__separator">
          <i title="分割线"></i>
          <div class="options">
            <ul>
              <li data-separator='0,0'>
                <i></i>
              </li>
              <li data-separator="1,1">
                <i></i>
              </li>
              <li data-separator="3,1">
                <i></i>
              </li>
              <li data-separator="4,4">
                <i></i>
              </li>
              <li data-separator="7,3,3,3">
                <i></i>
              </li>
              <li data-separator="6,2,2,2,2,2">
                <i></i>
              </li>
            </ul>
          </div>
        </div>
        <div class="menu-item__watermark">
          <i title="水印(添加、删除)"></i>
          <div class="options">
            <ul>
              <li data-menu="add">添加水印</li>
              <li data-menu="delete">删除水印</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__codeblock" title="代码块">
          <i></i>
        </div>
        <div class="menu-item__page-break" title="分页符">
          <i></i>
        </div>
        <div class="menu-item__control">
          <i title="控件"></i>
          <div class="options">
            <ul>
              <li data-control='text'>文本</li>
              <li data-control="select">列举</li>
              <li data-control="date">日期</li>
              <li data-control="checkbox">复选框</li>
              <li data-control="radio">单选框</li>
            </ul>
          </div>
        </div>
        <div class="menu-item__checkbox" title="复选框">
          <i></i>
        </div>
        <div class="menu-item__radio" title="单选框">
          <i></i>
        </div>
        <div class="menu-item__latex" title="LateX">
          <i></i>
        </div>
        <div class="menu-item__date">
          <i title="日期"></i>
          <div class="options">
            <ul>
              <li data-format="yyyy-MM-dd"></li>
              <li data-format="yyyy-MM-dd hh:mm:ss"></li>
            </ul>
          </div>
        </div>
        <div class="menu-item__block" title="内容块">
          <i></i>
        </div>
      </div>
      <div class="menu-divider"></div>
      <div class="menu-item">
        <div class="menu-item__search" data-menu="search">
          <i></i>
        </div>
        <div class="menu-item__search__collapse" data-menu="search">
          <div class="menu-item__search__collapse__search">
            <input type="text" />
            <label class="search-result"></label>
            <div class="arrow-left">
              <i></i>
            </div>
            <div class="arrow-right">
              <i></i>
            </div>
            <span>×</span>
          </div>
          <div class="menu-item__search__collapse__replace">
            <input type="text">
            <button>替换</button>
          </div>
        </div>
        <div class="menu-item__print" data-menu="print">
          <i></i>
        </div>
      </div>
    </div>
    <div class="catalog" editor-component="catalog">
      <div class="catalog__header">
        <span>目录</span>
        <div class="catalog__header__close">
          <i></i>
        </div>
      </div>
      <div class="catalog__main"></div>
    </div>
    <div class="editor"></div>
    <div class="comment" editor-component="comment"></div>
    <div class="footer" editor-component="footer">
      <div>
        <div class="catalog-mode" title="目录">
          <i></i>
        </div>
        <div class="page-mode">
          <i title="页面模式(分页、连页)"></i>
          <div class="options">
            <ul>
              <li data-page-mode="paging" class="active">分页</li>
              <li data-page-mode="continuity">连页</li>
            </ul>
          </div>
        </div>
        <span>可见页码：<span class="page-no-list">1</span></span>
        <span>页面：<span class="page-no">1</span>/<span class="page-size">1</span></span>
        <span>字数：<span class="word-count">0</span></span>
      </div>
      <div class="editor-mode" title="编辑模式(编辑、清洁、只读、表单、设计)">编辑模式</div>
      <div>
        <div class="page-scale-minus" title="缩小(Ctrl+-)">
          <i></i>
        </div>
        <span class="page-scale-percentage" title="显示比例(点击可复原Ctrl+0)">100%</span>
        <div class="page-scale-add" title="放大(Ctrl+=)">
          <i></i>
        </div>
        <div class="paper-size">
          <i title="纸张类型"></i>
          <div class="options">
            <ul>
              <li data-paper-size="794*1123" class="active">A4</li>
              <li data-paper-size="1593*2251">A2</li>
              <li data-paper-size="1125*1593">A3</li>
              <li data-paper-size="565*796">A5</li>
              <li data-paper-size="412*488">5号信封</li>
              <li data-paper-size="450*866">6号信封</li>
              <li data-paper-size="609*862">7号信封</li>
              <li data-paper-size="862*1221">9号信封</li>
              <li data-paper-size="813*1266">法律用纸</li>
              <li data-paper-size="813*1054">信纸</li>
            </ul>
          </div>
        </div>
        <div class="paper-direction">
          <i title="纸张方向"></i>
          <div class="options">
            <ul>
              <li data-paper-direction="vertical" class="active">纵向</li>
              <li data-paper-direction="horizontal">横向</li>
            </ul>
          </div>
        </div>
        <div class="paper-margin" title="页边距">
          <i></i>
        </div>
        <div class="fullscreen" title="全屏显示">
          <i></i>
        </div>
        <div class="editor-option" title="编辑器设置">
          <i></i>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Editor from '@hufe921/canvas-editor'
import './style.css'

export default {
  name: "CanvasEditor",
  data() {
    return {
      // 字体列表
      fontFamilyList: [
        {
          value: 'Microsoft YaHei',
          label: '微软雅黑'
        },
        {
          value: '华文宋体',
          label: '华文宋体'
        },
        {
          value: '华文黑体',
          label: '华文黑体'
        },
        {
          value: '华文仿宋',
          label: '华文仿宋'
        },
        {
          value: '华文楷体',
          label: '华文楷体'
        },
        {
          value: '华文琥珀',
          label: '华文琥珀'
        },
        {
          value: '华文隶书',
          label: '华文隶书'
        },
        {
          value: '华文新魏',
          label: '华文新魏'
        },
        {
          value: '华文行楷',
          label: '华文行楷'
        },
        {
          value: '华文中宋',
          label: '华文中宋'
        },
        {
          value: '华文彩云',
          label: '华文彩云'
        },
        {
          value: 'Arial',
          label: 'Arial'
        },
        {
          value: 'Segoe UI',
          label: 'Segoe UI'
        },
        {
          value: 'Ink Free',
          label: 'Ink Free'
        },
        {
          value: 'Fantasy',
          label: 'Fantasy'
        }
      ],
      // 字号列表
      fontSizeList: [
        {
          value: 56,
          label: '初号'
        },
        {
          value: 48,
          label: '小初'
        },
        {
          value: 34,
          label: '一号'
        },
        {
          value: 32,
          label: '小一'
        },
        {
          value: 29,
          label: '二号'
        },
        {
          value: 24,
          label: '小二'
        },
        {
          value: 21,
          label: '三号'
        },
        {
          value: 20,
          label: '小三'
        },
        {
          value: 18,
          label: '四号'
        },
        {
          value: 16,
          label: '小四'
        },
        {
          value: 14,
          label: '五号'
        },
        {
          value: 12,
          label: '小五'
        },
        {
          value: 10,
          label: '六号'
        },
        {
          value: 8,
          label: '小六'
        },
        {
          value: 7,
          label: '七号'
        },
        {
          value: 6,
          label: '八号'
        },
      ],
      data: [],
      options: {
        mode: "edit",
        defaultType: "TEXT",
        defaultColor: "#000000",
        defaultFont: "Microsoft YaHei",
        defaultSize: 16,
        minSize: 5,
        maxSize: 72,
        defaultRowMargin: 1,
        defaultBasicRowMarginHeight: 8,
        defaultTabWidth: 32,
        width: 794,
        height: 1123,
        scale: 1,
        pageGap: 20,
        underlineColor: "#000000",
        strikeoutColor: "#FF0000",
        rangeAlpha: 0.6,
        rangeColor: "#AECBFA",
        rangeMinWidth: 5,
        searchMatchAlpha: 0.6,
        searchMatchColor: "#FFFF00",
        searchNavigateMatchColor: "#AAD280",
        highlightAlpha: 0.6,
        resizerColor: "#4182D9",
        resizerSize: 5,
        marginIndicatorSize: 35,
        marginIndicatorColor: "#BABABA",
        margins: [
          100,
          120,
          100,
          120
        ],
        pageMode: "paging",
        renderMode: "speed",
        defaultHyperlinkColor: "#0000FF",
        paperDirection: "vertical",
        inactiveAlpha: 0.6,
        historyMaxRecordCount: 100,
        wordBreak: "break-word",
        printPixelRatio: 3,
        maskMargin: [
          60,
          0,
          30,
          0
        ],
        letterClass: [
          "A-Za-z"
        ],
        contextMenuDisableKeys: [],
        scrollContainerSelector: "",
        // "watermark": {
        //   "data": "CANVAS-EDITOR",
        //   "color": "#AEB5C0",
        //   "opacity": 0.3,
        //   "size": 120,
        //   "font": "Microsoft YaHei",
        //   "repeat": false,
        //   "gap": [
        //     10,
        //     10
        //   ],
        //   "numberType": "arabic"
        // },
        pageNumber: {
          bottom: 60,
          size: 12,
          font: "Microsoft YaHei",
          color: "#000000",
          rowFlex: "center",
          format: "第{pageNo}页/共{pageCount}页",
          numberType: "arabic",
          disabled: false,
          startPageNo: 1,
          fromPageNo: 0,
          maxPageNo: null
        },
        placeholder: {
          data: "请输入正文",
          color: "#DCDFE6",
          opacity: 1,
          size: 16,
          font: "Microsoft YaHei"
        },
        zone: {
          "tipDisabled": false
        },
        table: {
          tdPadding: [
            0,
            5,
            5,
            5
          ],
          defaultTrMinHeight: 42,
          defaultColMinWidth: 40,
          defaultBorderColor: "#000000"
        },
        header: {
          top: 30,
          maxHeightRadio: "half",
          disabled: false,
          editable: true
        },
        footer: {
          bottom: 30,
          maxHeightRadio: "half",
          disabled: false,
          editable: true
        },
        control: {
          placeholderColor: "#9c9b9b",
          bracketColor: "#000000",
          prefix: "{",
          postfix: "}",
          borderWidth: 1,
          borderColor: "#000000",
          activeBackgroundColor: ""
        },
        checkbox: {
          width: 14,
          height: 14,
          gap: 5,
          lineWidth: 1,
          fillStyle: "#5175f4",
          strokeStyle: "#ffffff",
          verticalAlign: "bottom"
        },
        radio: {
          width: 14,
          height: 14,
          gap: 5,
          lineWidth: 1,
          fillStyle: "#5175f4",
          strokeStyle: "#000000",
          verticalAlign: "bottom"
        },
        cursor: {
          width: 1,
          color: "#000000",
          dragWidth: 2,
          dragColor: "#0000FF",
          dragFloatImageDisabled: false
        },
        title: {
          defaultFirstSize: 26,
          defaultSecondSize: 24,
          defaultThirdSize: 22,
          defaultFourthSize: 20,
          defaultFifthSize: 18,
          defaultSixthSize: 16
        },
        group: {
          opacity: 0.1,
          backgroundColor: "#E99D00",
          activeOpacity: 0.5,
          activeBackgroundColor: "#E99D00",
          disabled: false
        },
        pageBreak: {
          font: "Microsoft YaHei",
          fontSize: 12,
          lineDash: [
            3,
            1
          ]
        },
        background: {
          color: "#FFFFFF",
          image: "",
          size: "cover",
          repeat: "no-repeat",
          applyPageNumbers: []
        },
        lineBreak: {
          disabled: true,
          color: "#CCCCCC",
          lineWidth: 1.5
        },
        separator: {
          lineWidth: 1,
          strokeStyle: "#000000"
        },
        lineNumber: {
          size: 12,
          font: "Microsoft YaHei",
          color: "#000000",
          disabled: true,
          right: 20,
          type: "continuity"
        },
        pageBorder: {
          color: "#000000",
          lineWidth: 1,
          padding: [
            0,
            5,
            0,
            5
          ],
          disabled: true
        },
        badge: {
          top: 0,
          left: 5
        }
      },
      instance: null
    };
  },
  props: {
    // options: {
    //   type: Object,
    //   default: () => ({
    //     mode: "edit",
    //     defaultType: "TEXT",
    //     defaultColor: "#000000",
    //     defaultFont: "Microsoft YaHei",
    //     defaultSize: 16,
    //     minSize: 5,
    //     maxSize: 72,
    //     defaultRowMargin: 1,
    //     defaultBasicRowMarginHeight: 8,
    //     defaultTabWidth: 32,
    //     width: 794,
    //     height: 1123,
    //     scale: 1,
    //     pageGap: 20,
    //     underlineColor: "#000000",
    //     strikeoutColor: "#FF0000",
    //     rangeAlpha: 0.6,
    //     rangeColor: "#AECBFA",
    //     rangeMinWidth: 5,
    //     searchMatchAlpha: 0.6,
    //     searchMatchColor: "#FFFF00",
    //     searchNavigateMatchColor: "#AAD280",
    //     highlightAlpha: 0.6,
    //     resizerColor: "#4182D9",
    //     resizerSize: 5,
    //     marginIndicatorSize: 35,
    //     marginIndicatorColor: "#BABABA",
    //     margins: [
    //       100,
    //       120,
    //       100,
    //       120
    //     ],
    //     pageMode: "paging",
    //     renderMode: "speed",
    //     defaultHyperlinkColor: "#0000FF",
    //     paperDirection: "vertical",
    //     inactiveAlpha: 0.6,
    //     historyMaxRecordCount: 100,
    //     wordBreak: "break-word",
    //     printPixelRatio: 3,
    //     maskMargin: [
    //       60,
    //       0,
    //       30,
    //       0
    //     ],
    //     letterClass: [
    //       "A-Za-z"
    //     ],
    //     contextMenuDisableKeys: [],
    //     scrollContainerSelector: "",
    //     // "watermark": {
    //     //   "data": "CANVAS-EDITOR",
    //     //   "color": "#AEB5C0",
    //     //   "opacity": 0.3,
    //     //   "size": 120,
    //     //   "font": "Microsoft YaHei",
    //     //   "repeat": false,
    //     //   "gap": [
    //     //     10,
    //     //     10
    //     //   ],
    //     //   "numberType": "arabic"
    //     // },
    //     pageNumber: {
    //       bottom: 60,
    //       size: 12,
    //       font: "Microsoft YaHei",
    //       color: "#000000",
    //       rowFlex: "center",
    //       format: "第{pageNo}页/共{pageCount}页",
    //       numberType: "arabic",
    //       disabled: false,
    //       startPageNo: 1,
    //       fromPageNo: 0,
    //       maxPageNo: null
    //     },
    //     placeholder: {
    //       data: "请输入正文",
    //       color: "#DCDFE6",
    //       opacity: 1,
    //       size: 16,
    //       font: "Microsoft YaHei"
    //     },
    //     zone: {
    //       "tipDisabled": false
    //     },
    //     table: {
    //       tdPadding: [
    //         0,
    //         5,
    //         5,
    //         5
    //       ],
    //       defaultTrMinHeight: 42,
    //       defaultColMinWidth: 40,
    //       defaultBorderColor: "#000000"
    //     },
    //     header: {
    //       top: 30,
    //       maxHeightRadio: "half",
    //       disabled: false,
    //       editable: true
    //     },
    //     footer: {
    //       bottom: 30,
    //       maxHeightRadio: "half",
    //       disabled: false,
    //       editable: true
    //     },
    //     control: {
    //       placeholderColor: "#9c9b9b",
    //       bracketColor: "#000000",
    //       prefix: "{",
    //       postfix: "}",
    //       borderWidth: 1,
    //       borderColor: "#000000",
    //       activeBackgroundColor: ""
    //     },
    //     checkbox: {
    //       width: 14,
    //       height: 14,
    //       gap: 5,
    //       lineWidth: 1,
    //       fillStyle: "#5175f4",
    //       strokeStyle: "#ffffff",
    //       verticalAlign: "bottom"
    //     },
    //     radio: {
    //       width: 14,
    //       height: 14,
    //       gap: 5,
    //       lineWidth: 1,
    //       fillStyle: "#5175f4",
    //       strokeStyle: "#000000",
    //       verticalAlign: "bottom"
    //     },
    //     cursor: {
    //       width: 1,
    //       color: "#000000",
    //       dragWidth: 2,
    //       dragColor: "#0000FF",
    //       dragFloatImageDisabled: false
    //     },
    //     title: {
    //       defaultFirstSize: 26,
    //       defaultSecondSize: 24,
    //       defaultThirdSize: 22,
    //       defaultFourthSize: 20,
    //       defaultFifthSize: 18,
    //       defaultSixthSize: 16
    //     },
    //     group: {
    //       opacity: 0.1,
    //       backgroundColor: "#E99D00",
    //       activeOpacity: 0.5,
    //       activeBackgroundColor: "#E99D00",
    //       disabled: false
    //     },
    //     pageBreak: {
    //       font: "Microsoft YaHei",
    //       fontSize: 12,
    //       lineDash: [
    //         3,
    //         1
    //       ]
    //     },
    //     background: {
    //       color: "#FFFFFF",
    //       image: "",
    //       size: "cover",
    //       repeat: "no-repeat",
    //       applyPageNumbers: []
    //     },
    //     lineBreak: {
    //       disabled: true,
    //       color: "#CCCCCC",
    //       lineWidth: 1.5
    //     },
    //     separator: {
    //       lineWidth: 1,
    //       strokeStyle: "#000000"
    //     },
    //     lineNumber: {
    //       size: 12,
    //       font: "Microsoft YaHei",
    //       color: "#000000",
    //       disabled: true,
    //       right: 20,
    //       type: "continuity"
    //     },
    //     pageBorder: {
    //       color: "#000000",
    //       lineWidth: 1,
    //       padding: [
    //         0,
    //         5,
    //         0,
    //         5
    //       ],
    //       disabled: true
    //     },
    //     badge: {
    //       top: 0,
    //       left: 5
    //     }
    //   })
    // },
    // data: {
    //   type: Array,
    //   default: () => []
    // },
    // getValue: {
    //   type: Function,
    //   default: () => {}
    // }
  },
  mounted() {
    this.instance = new Editor(document.querySelector('.editor'),
      this.data,
      this.options
    )

    this.init()
  },
  methods: {
    async clickHandler() {
      const { version, data, options } = await this.instance.command.getValueAsync()
      const editorOption = await this.instance.command.getOptions()
      console.log(options, data, editorOption);
      // this.$emit()
    },
    init() {
      const isApple =
        typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent)

      // 1. 初始化编辑器
      const container = document.querySelector('.editor')
      const instance = new Editor(
        container,
        this.data,
        this.options
      )
      // console.log('实例: ', instance)
      // cypress使用
      Reflect.set(window, 'editor', instance)

      // 菜单弹窗销毁
      window.addEventListener(
        'click',
        evt => {
          const visibleDom = document.querySelector('.visible')
          if (!visibleDom || visibleDom.contains(evt.target)) return
          visibleDom.classList.remove('visible')
        },
        {
          capture: true
        }
      )

      // 2. | 撤销 | 重做 | 格式刷 | 清除格式 |
      const undoDom = document.querySelector('.menu-item__undo')
      undoDom.title = `撤销(${isApple ? '⌘' : 'Ctrl'}+Z)`
      undoDom.onclick = function () {
        console.log('undo')
        instance.command.executeUndo()
      }

      const redoDom = document.querySelector('.menu-item__redo')
      redoDom.title = `重做(${isApple ? '⌘' : 'Ctrl'}+Y)`
      redoDom.onclick = function () {
        console.log('redo')
        instance.command.executeRedo()
      }

      const painterDom = document.querySelector(
        '.menu-item__painter'
      )

      let isFirstClick = true
      let painterTimeout
      painterDom.onclick = function () {
        if (isFirstClick) {
          isFirstClick = false
          painterTimeout = window.setTimeout(() => {
            console.log('painter-click')
            isFirstClick = true
            instance.command.executePainter({
              isDblclick: false
            })
          }, 200)
        } else {
          window.clearTimeout(painterTimeout)
        }
      }

      painterDom.ondblclick = function () {
        console.log('painter-dblclick')
        isFirstClick = true
        window.clearTimeout(painterTimeout)
        instance.command.executePainter({
          isDblclick: true
        })
      }

      document.querySelector('.menu-item__format').onclick =
        function () {
          console.log('format')
          instance.command.executeFormat()
        }

      // 3. | 字体 | 字体变大 | 字体变小 | 加粗 | 斜体 | 下划线 | 删除线 | 上标 | 下标 | 字体颜色 | 背景色 |
      const fontDom = document.querySelector('.menu-item__font')
      const fontSelectDom = fontDom.querySelector('.select')
      const fontOptionDom = fontDom.querySelector('.options')
      fontDom.onclick = function () {
        console.log('font')
        fontOptionDom.classList.toggle('visible')
      }
      fontOptionDom.onclick = function (evt) {
        const li = evt.target
        instance.command.executeFont(li.dataset.family)
      }

      const sizeSetDom = document.querySelector('.menu-item__size')
      const sizeSelectDom = sizeSetDom.querySelector('.select')
      const sizeOptionDom = sizeSetDom.querySelector('.options')
      sizeSetDom.title = `设置字号`
      sizeSetDom.onclick = function () {
        console.log('size')
        sizeOptionDom.classList.toggle('visible')
      }
      sizeOptionDom.onclick = function (evt) {
        const li = evt.target
        instance.command.executeSize(Number(li.dataset.size))
      }

      const sizeAddDom = document.querySelector(
        '.menu-item__size-add'
      )
      sizeAddDom.title = `增大字号(${isApple ? '⌘' : 'Ctrl'}+[)`
      sizeAddDom.onclick = function () {
        console.log('size-add')
        instance.command.executeSizeAdd()
      }

      const sizeMinusDom = document.querySelector(
        '.menu-item__size-minus'
      )
      sizeMinusDom.title = `减小字号(${isApple ? '⌘' : 'Ctrl'}+])`
      sizeMinusDom.onclick = function () {
        console.log('size-minus')
        instance.command.executeSizeMinus()
      }

      const boldDom = document.querySelector('.menu-item__bold')
      boldDom.title = `加粗(${isApple ? '⌘' : 'Ctrl'}+B)`
      boldDom.onclick = function () {
        console.log('bold')
        instance.command.executeBold()
      }

      const italicDom =
        document.querySelector('.menu-item__italic')
      italicDom.title = `斜体(${isApple ? '⌘' : 'Ctrl'}+I)`
      italicDom.onclick = function () {
        console.log('italic')
        instance.command.executeItalic()
      }

      const underlineDom = document.querySelector(
        '.menu-item__underline'
      )
      underlineDom.title = `下划线(${isApple ? '⌘' : 'Ctrl'}+U)`
      const underlineOptionDom =
        underlineDom.querySelector('.options')
      underlineDom.querySelector('.select').onclick =
        function () {
          underlineOptionDom.classList.toggle('visible')
        }
      underlineDom.querySelector('i').onclick = function () {
        console.log('underline')
        instance.command.executeUnderline()
        underlineOptionDom.classList.remove('visible')
      }
      underlineDom.querySelector('ul').onmousedown = function (
        evt
      ) {
        const li = evt.target
        const decorationStyle = li.dataset.decorationStyle
        instance.command.executeUnderline({
          style: decorationStyle
        })
        underlineOptionDom.classList.remove('visible')
      }

      const strikeoutDom = document.querySelector(
        '.menu-item__strikeout'
      )
      strikeoutDom.onclick = function () {
        console.log('strikeout')
        instance.command.executeStrikeout()
      }

      const superscriptDom = document.querySelector(
        '.menu-item__superscript'
      )
      superscriptDom.title = `上标(${isApple ? '⌘' : 'Ctrl'}+Shift+,)`
      superscriptDom.onclick = function () {
        console.log('superscript')
        instance.command.executeSuperscript()
      }

      const subscriptDom = document.querySelector(
        '.menu-item__subscript'
      )
      subscriptDom.title = `下标(${isApple ? '⌘' : 'Ctrl'}+Shift+.)`
      subscriptDom.onclick = function () {
        console.log('subscript')
        instance.command.executeSubscript()
      }

      const colorControlDom = document.querySelector < HTMLInputElement > ('#color')
      colorControlDom.oninput = function () {
        instance.command.executeColor(colorControlDom.value)
      }
      const colorDom = document.querySelector('.menu-item__color')
      const colorSpanDom = colorDom.querySelector('span')
      colorDom.onclick = function () {
        console.log('color')
        colorControlDom.click()
      }

      const highlightControlDom =
        document.querySelector < HTMLInputElement > ('#highlight')
      highlightControlDom.oninput = function () {
        instance.command.executeHighlight(highlightControlDom.value)
      }
      const highlightDom = document.querySelector(
        '.menu-item__highlight'
      )
      const highlightSpanDom = highlightDom.querySelector('span')
      highlightDom.onclick = function () {
        console.log('highlight')
        highlightControlDom?.click()
      }

      const titleDom = document.querySelector('.menu-item__title')
      const titleSelectDom = titleDom.querySelector('.select')
      const titleOptionDom = titleDom.querySelector('.options')
      titleOptionDom.querySelectorAll('li').forEach((li, index) => {
        li.title = `Ctrl+${isApple ? 'Option' : 'Alt'}+${index}`
      })

      titleDom.onclick = function () {
        console.log('title')
        titleOptionDom.classList.toggle('visible')
      }
      titleOptionDom.onclick = function (evt) {
        const li = evt.target
        const level = li.dataset.level
        instance.command.executeTitle(level || null)
      }

      const leftDom = document.querySelector('.menu-item__left')
      leftDom.title = `左对齐(${isApple ? '⌘' : 'Ctrl'}+L)`
      leftDom.onclick = function () {
        console.log('left')
        instance.command.executeRowFlex(RowFlex.LEFT)
      }

      const centerDom =
        document.querySelector('.menu-item__center')
      centerDom.title = `居中对齐(${isApple ? '⌘' : 'Ctrl'}+E)`
      centerDom.onclick = function () {
        console.log('center')
        instance.command.executeRowFlex(RowFlex.CENTER)
      }

      const rightDom = document.querySelector('.menu-item__right')
      rightDom.title = `右对齐(${isApple ? '⌘' : 'Ctrl'}+R)`
      rightDom.onclick = function () {
        console.log('right')
        instance.command.executeRowFlex(RowFlex.RIGHT)
      }

      const alignmentDom = document.querySelector(
        '.menu-item__alignment'
      )
      alignmentDom.title = `两端对齐(${isApple ? '⌘' : 'Ctrl'}+J)`
      alignmentDom.onclick = function () {
        console.log('alignment')
        instance.command.executeRowFlex(RowFlex.ALIGNMENT)
      }

      const justifyDom = document.querySelector(
        '.menu-item__justify'
      )
      justifyDom.title = `分散对齐(${isApple ? '⌘' : 'Ctrl'}+Shift+J)`
      justifyDom.onclick = function () {
        console.log('justify')
        instance.command.executeRowFlex(RowFlex.JUSTIFY)
      }

      const rowMarginDom = document.querySelector(
        '.menu-item__row-margin'
      )
      const rowOptionDom = rowMarginDom.querySelector('.options')
      rowMarginDom.onclick = function () {
        console.log('row-margin')
        rowOptionDom.classList.toggle('visible')
      }
      rowOptionDom.onclick = function (evt) {
        const li = evt.target
        instance.command.executeRowMargin(Number(li.dataset.rowmargin))
      }

      const listDom = document.querySelector('.menu-item__list')
      listDom.title = `列表(${isApple ? '⌘' : 'Ctrl'}+Shift+U)`
      const listOptionDom = listDom.querySelector('.options')
      listDom.onclick = function () {
        console.log('list')
        listOptionDom.classList.toggle('visible')
      }
      listOptionDom.onclick = function (evt) {
        const li = evt.target
        const listType = li.dataset.listType || null
        const listStyle = (li.dataset.listStyle)
        instance.command.executeList(listType, listStyle)
      }

      // 4. | 表格 | 图片 | 超链接 | 分割线 | 水印 | 代码块 | 分隔符 | 控件 | 复选框 | LaTeX | 日期选择器
      const tableDom = document.querySelector('.menu-item__table')
      const tablePanelContainer = document.querySelector(
        '.menu-item__table__collapse'
      )
      const tableClose = document.querySelector('.table-close')
      const tableTitle = document.querySelector('.table-select')
      const tablePanel = document.querySelector('.table-panel')
      // 绘制行列
      const tableCellList = []
      for (let i = 0; i < 10; i++) {
        const tr = document.createElement('tr')
        tr.classList.add('table-row')
        const trCellList = []
        for (let j = 0; j < 10; j++) {
          const td = document.createElement('td')
          td.classList.add('table-cel')
          tr.append(td)
          trCellList.push(td)
        }
        tablePanel.append(tr)
        tableCellList.push(trCellList)
      }
      let colIndex = 0
      let rowIndex = 0
      // 移除所有格选择
      function removeAllTableCellSelect() {
        tableCellList.forEach(tr => {
          tr.forEach(td => td.classList.remove('active'))
        })
      }
      // 设置标题内容
      function setTableTitle(payload) {
        tableTitle.innerText = payload
      }
      // 恢复初始状态
      function recoveryTable() {
        // 还原选择样式、标题、选择行列
        removeAllTableCellSelect()
        setTableTitle('插入')
        colIndex = 0
        rowIndex = 0
        // 隐藏panel
        tablePanelContainer.style.display = 'none'
      }
      tableDom.onclick = function () {
        console.log('table')
        tablePanelContainer.style.display = 'block'
      }
      tablePanel.onmousemove = function (evt) {
        const celSize = 16
        const rowMarginTop = 10
        const celMarginRight = 6
        const { offsetX, offsetY } = evt
        // 移除所有选择
        removeAllTableCellSelect()
        colIndex = Math.ceil(offsetX / (celSize + celMarginRight)) || 1
        rowIndex = Math.ceil(offsetY / (celSize + rowMarginTop)) || 1
        // 改变选择样式
        tableCellList.forEach((tr, trIndex) => {
          tr.forEach((td, tdIndex) => {
            if (tdIndex < colIndex && trIndex < rowIndex) {
              td.classList.add('active')
            }
          })
        })
        // 改变表格标题
        setTableTitle(`${rowIndex}×${colIndex}`)
      }
      tableClose.onclick = function () {
        recoveryTable()
      }
      tablePanel.onclick = function () {
        // 应用选择
        instance.command.executeInsertTable(rowIndex, colIndex)
        recoveryTable()
      }

      const imageDom = document.querySelector('.menu-item__image')
      const imageFileDom = document.querySelector < HTMLInputElement > ('#image')
      imageDom.onclick = function () {
        imageFileDom.click()
      }
      imageFileDom.onchange = function () {
        const file = imageFileDom.files[0]
        const fileReader = new FileReader()
        fileReader.readAsDataURL(file)
        fileReader.onload = function () {
          // 计算宽高
          const image = new Image()
          const value = fileReader.result
          image.src = value
          image.onload = function () {
            instance.command.executeImage({
              value,
              width: image.width,
              height: image.height
            })
            imageFileDom.value = ''
          }
        }
      }

      const hyperlinkDom = document.querySelector(
        '.menu-item__hyperlink'
      )
      hyperlinkDom.onclick = function () {
        console.log('hyperlink')
        new Dialog({
          title: '超链接',
          data: [
            {
              type: 'text',
              label: '文本',
              name: 'name',
              required: true,
              placeholder: '请输入文本',
              value: instance.command.getRangeText()
            },
            {
              type: 'text',
              label: '链接',
              name: 'url',
              required: true,
              placeholder: '请输入链接'
            }
          ],
          onConfirm: payload => {
            const name = payload.find(p => p.name === 'name')?.value
            if (!name) return
            const url = payload.find(p => p.name === 'url')?.value
            if (!url) return
            instance.command.executeHyperlink({
              type: ElementType.HYPERLINK,
              value: '',
              url,
              valueList: splitText(name).map(n => ({
                value: n,
                size: 16
              }))
            })
          }
        })
      }

      const separatorDom = document.querySelector(
        '.menu-item__separator'
      )
      const separatorOptionDom =
        separatorDom.querySelector('.options')
      separatorDom.onclick = function () {
        console.log('separator')
        separatorOptionDom.classList.toggle('visible')
      }
      separatorOptionDom.onmousedown = function (evt) {
        let payload = []
        const li = evt.target
        const separatorDash = li.dataset.separator?.split(',').map(Number)
        if (separatorDash) {
          const isSingleLine = separatorDash.every(d => d === 0)
          if (!isSingleLine) {
            payload = separatorDash
          }
        }
        instance.command.executeSeparator(payload)
      }

      const pageBreakDom = document.querySelector(
        '.menu-item__page-break'
      )
      pageBreakDom.onclick = function () {
        console.log('pageBreak')
        instance.command.executePageBreak()
      }

      const watermarkDom = document.querySelector(
        '.menu-item__watermark'
      )
      const watermarkOptionDom =
        watermarkDom.querySelector('.options')
      watermarkDom.onclick = function () {
        console.log('watermark')
        watermarkOptionDom.classList.toggle('visible')
      }
      watermarkOptionDom.onmousedown = function (evt) {
        const li = evt.target
        const menu = li.dataset.menu
        watermarkOptionDom.classList.toggle('visible')
        if (menu === 'add') {
          new Dialog({
            title: '水印',
            data: [
              {
                type: 'text',
                label: '内容',
                name: 'data',
                required: true,
                placeholder: '请输入内容'
              },
              {
                type: 'color',
                label: '颜色',
                name: 'color',
                required: true,
                value: '#AEB5C0'
              },
              {
                type: 'number',
                label: '字体大小',
                name: 'size',
                required: true,
                value: '120'
              }
            ],
            onConfirm: payload => {
              const nullableIndex = payload.findIndex(p => !p.value)
              if (~nullableIndex) return
              const watermark = payload.reduce((pre, cur) => {
                pre[cur.name] = cur.value
                return pre
              }, {})
              instance.command.executeAddWatermark({
                data: watermark.data,
                color: watermark.color,
                // size(watermark.size)
                size: watermark.size
              })
            }
          })
        } else {
          instance.command.executeDeleteWatermark()
        }
      }

      const codeblockDom = document.querySelector(
        '.menu-item__codeblock'
      )
      codeblockDom.onclick = function () {
        console.log('codeblock')
        new Dialog({
          title: '代码块',
          data: [
            {
              type: 'textarea',
              name: 'codeblock',
              placeholder: '请输入代码',
              width: 500,
              height: 300
            }
          ],
          onConfirm: payload => {
            const codeblock = payload.find(p => p.name === 'codeblock')?.value
            if (!codeblock) return
            const tokenList = prism.tokenize(codeblock, prism.languages.javascript)
            const formatTokenList = formatPrismToken(tokenList)
            const elementList = []
            for (let i = 0; i < formatTokenList.length; i++) {
              const formatToken = formatTokenList[i]
              const tokenStringList = splitText(formatToken.content)
              for (let j = 0; j < tokenStringList.length; j++) {
                const value = tokenStringList[j]
                const element = {
                  value
                }
                if (formatToken.color) {
                  element.color = formatToken.color
                }
                if (formatToken.bold) {
                  element.bold = true
                }
                if (formatToken.italic) {
                  element.italic = true
                }
                elementList.push(element)
              }
            }
            elementList.unshift({
              value: '\n'
            })
            instance.command.executeInsertElementList(elementList)
          }
        })
      }

      const controlDom = document.querySelector(
        '.menu-item__control'
      )
      const controlOptionDom = controlDom.querySelector('.options')
      controlDom.onclick = function () {
        console.log('control')
        controlOptionDom.classList.toggle('visible')
      }
      controlOptionDom.onmousedown = function (evt) {
        controlOptionDom.classList.toggle('visible')
        const li = evt.target
        const type = li.dataset.control
        switch (type) {
          case ControlType.TEXT:
            new Dialog({
              title: '文本控件',
              data: [
                {
                  type: 'text',
                  label: '占位符',
                  name: 'placeholder',
                  required: true,
                  placeholder: '请输入占位符'
                },
                {
                  type: 'text',
                  label: '默认值',
                  name: 'value',
                  placeholder: '请输入默认值'
                }
              ],
              onConfirm: payload => {
                const placeholder = payload.find(
                  p => p.name === 'placeholder'
                )?.value
                if (!placeholder) return
                const value = payload.find(p => p.name === 'value')?.value || ''
                instance.command.executeInsertElementList([
                  {
                    type: ElementType.CONTROL,
                    value: '',
                    control: {
                      type,
                      value: value
                        ? [
                          {
                            value
                          }
                        ]
                        : null,
                      placeholder
                    }
                  }
                ])
              }
            })
            break
          case ControlType.SELECT:
            new Dialog({
              title: '列举控件',
              data: [
                {
                  type: 'text',
                  label: '占位符',
                  name: 'placeholder',
                  required: true,
                  placeholder: '请输入占位符'
                },
                {
                  type: 'text',
                  label: '默认值',
                  name: 'code',
                  placeholder: '请输入默认值'
                },
                {
                  type: 'textarea',
                  label: '值集',
                  name: 'valueSets',
                  required: true,
                  height: 100,
                  placeholder: `请输入值集JSON，例：\n[{\n"value":"有",\n"code":"98175"\n}]`
                }
              ],
              onConfirm: payload => {
                const placeholder = payload.find(
                  p => p.name === 'placeholder'
                )?.value
                if (!placeholder) return
                const valueSets = payload.find(p => p.name === 'valueSets')?.value
                if (!valueSets) return
                const code = payload.find(p => p.name === 'code')?.value
                instance.command.executeInsertElementList([
                  {
                    type: ElementType.CONTROL,
                    value: '',
                    control: {
                      type,
                      code,
                      value: null,
                      placeholder,
                      valueSets: JSON.parse(valueSets)
                    }
                  }
                ])
              }
            })
            break
          case ControlType.CHECKBOX:
            new Dialog({
              title: '复选框控件',
              data: [
                {
                  type: 'text',
                  label: '默认值',
                  name: 'code',
                  placeholder: '请输入默认值，多个值以英文逗号分割'
                },
                {
                  type: 'textarea',
                  label: '值集',
                  name: 'valueSets',
                  required: true,
                  height: 100,
                  placeholder: `请输入值集JSON，例：\n[{\n"value":"有",\n"code":"98175"\n}]`
                }
              ],
              onConfirm: payload => {
                const valueSets = payload.find(p => p.name === 'valueSets')?.value
                if (!valueSets) return
                const code = payload.find(p => p.name === 'code')?.value
                instance.command.executeInsertElementList([
                  {
                    type: ElementType.CONTROL,
                    value: '',
                    control: {
                      type,
                      code,
                      value: null,
                      valueSets: JSON.parse(valueSets)
                    }
                  }
                ])
              }
            })
            break
          case ControlType.RADIO:
            new Dialog({
              title: '单选框控件',
              data: [
                {
                  type: 'text',
                  label: '默认值',
                  name: 'code',
                  placeholder: '请输入默认值'
                },
                {
                  type: 'textarea',
                  label: '值集',
                  name: 'valueSets',
                  required: true,
                  height: 100,
                  placeholder: `请输入值集JSON，例：\n[{\n"value":"有",\n"code":"98175"\n}]`
                }
              ],
              onConfirm: payload => {
                const valueSets = payload.find(p => p.name === 'valueSets')?.value
                if (!valueSets) return
                const code = payload.find(p => p.name === 'code')?.value
                instance.command.executeInsertElementList([
                  {
                    type: ElementType.CONTROL,
                    value: '',
                    control: {
                      type,
                      code,
                      value: null,
                      valueSets: JSON.parse(valueSets)
                    }
                  }
                ])
              }
            })
            break
          case ControlType.DATE:
            new Dialog({
              title: '日期控件',
              data: [
                {
                  type: 'text',
                  label: '占位符',
                  name: 'placeholder',
                  required: true,
                  placeholder: '请输入占位符'
                },
                {
                  type: 'text',
                  label: '默认值',
                  name: 'value',
                  placeholder: '请输入默认值'
                },
                {
                  type: 'select',
                  label: '日期格式',
                  name: 'dateFormat',
                  value: 'yyyy-MM-dd hh:mm:ss',
                  required: true,
                  options: [
                    {
                      label: 'yyyy-MM-dd hh:mm:ss',
                      value: 'yyyy-MM-dd hh:mm:ss'
                    },
                    {
                      label: 'yyyy-MM-dd',
                      value: 'yyyy-MM-dd'
                    }
                  ]
                }
              ],
              onConfirm: payload => {
                const placeholder = payload.find(
                  p => p.name === 'placeholder'
                )?.value
                if (!placeholder) return
                const value = payload.find(p => p.name === 'value')?.value || ''
                const dateFormat =
                  payload.find(p => p.name === 'dateFormat')?.value || ''
                instance.command.executeInsertElementList([
                  {
                    type: ElementType.CONTROL,
                    value: '',
                    control: {
                      type,
                      dateFormat,
                      value: value
                        ? [
                          {
                            value
                          }
                        ]
                        : null,
                      placeholder
                    }
                  }
                ])
              }
            })
            break
          default:
            break
        }
      }

      const checkboxDom = document.querySelector(
        '.menu-item__checkbox'
      )
      checkboxDom.onclick = function () {
        console.log('checkbox')
        instance.command.executeInsertElementList([
          {
            type: ElementType.CHECKBOX,
            checkbox: {
              value: false
            },
            value: ''
          }
        ])
      }

      const radioDom = document.querySelector('.menu-item__radio')
      radioDom.onclick = function () {
        console.log('radio')
        instance.command.executeInsertElementList([
          {
            type: ElementType.RADIO,
            checkbox: {
              value: false
            },
            value: ''
          }
        ])
      }

      const latexDom = document.querySelector('.menu-item__latex')
      latexDom.onclick = function () {
        console.log('LaTeX')
        new Dialog({
          title: 'LaTeX',
          data: [
            {
              type: 'textarea',
              height: 100,
              name: 'value',
              placeholder: '请输入LaTeX文本'
            }
          ],
          onConfirm: payload => {
            const value = payload.find(p => p.name === 'value')?.value
            if (!value) return
            instance.command.executeInsertElementList([
              {
                type: ElementType.LATEX,
                value
              }
            ])
          }
        })
      }

      const dateDom = document.querySelector('.menu-item__date')
      const dateDomOptionDom = dateDom.querySelector('.options')
      dateDom.onclick = function () {
        console.log('date')
        dateDomOptionDom.classList.toggle('visible')
        // 定位调整
        const bodyRect = document.body.getBoundingClientRect()
        const dateDomOptionRect = dateDomOptionDom.getBoundingClientRect()
        if (dateDomOptionRect.left + dateDomOptionRect.width > bodyRect.width) {
          dateDomOptionDom.style.right = '0px'
          dateDomOptionDom.style.left = 'unset'
        } else {
          dateDomOptionDom.style.right = 'unset'
          dateDomOptionDom.style.left = '0px'
        }
        // 当前日期
        const date = new Date()
        const year = date.getFullYear().toString()
        const month = (date.getMonth() + 1).toString().padStart(2, '0')
        const day = date.getDate().toString().padStart(2, '0')
        const hour = date.getHours().toString().padStart(2, '0')
        const minute = date.getMinutes().toString().padStart(2, '0')
        const second = date.getSeconds().toString().padStart(2, '0')
        const dateString = `${year}-${month}-${day}`
        const dateTimeString = `${dateString} ${hour}:${minute}:${second}`
        dateDomOptionDom.querySelector('li:first-child').innerText =
          dateString
        dateDomOptionDom.querySelector('li:last-child').innerText =
          dateTimeString
      }
      dateDomOptionDom.onmousedown = function (evt) {
        const li = evt.target
        const dateFormat = li.dataset.format
        dateDomOptionDom.classList.toggle('visible')
        instance.command.executeInsertElementList([
          {
            type: ElementType.DATE,
            value: '',
            dateFormat,
            valueList: [
              {
                value: li.innerText.trim()
              }
            ]
          }
        ])
      }

      const blockDom = document.querySelector('.menu-item__block')
      blockDom.onclick = function () {
        console.log('block')
        new Dialog({
          title: '内容块',
          data: [
            {
              type: 'select',
              label: '类型',
              name: 'type',
              value: 'iframe',
              required: true,
              options: [
                {
                  label: '网址',
                  value: 'iframe'
                },
                {
                  label: '视频',
                  value: 'video'
                }
              ]
            },
            {
              type: 'number',
              label: '宽度',
              name: 'width',
              placeholder: '请输入宽度（默认页面内宽度）'
            },
            {
              type: 'number',
              label: '高度',
              name: 'height',
              required: true,
              placeholder: '请输入高度'
            },
            {
              type: 'input',
              label: '地址',
              name: 'src',
              required: false,
              placeholder: '请输入地址'
            },
            {
              type: 'textarea',
              label: 'HTML',
              height: 100,
              name: 'srcdoc',
              required: false,
              placeholder: '请输入HTML代码（仅网址类型有效）'
            }
          ],
          onConfirm: payload => {
            const type = payload.find(p => p.name === 'type')?.value
            if (!type) return
            const width = payload.find(p => p.name === 'width')?.value
            const height = payload.find(p => p.name === 'height')?.value
            if (!height) return
            // 地址或HTML代码至少存在一项
            const src = payload.find(p => p.name === 'src')?.value
            const srcdoc = payload.find(p => p.name === 'srcdoc')?.value
            const block = {
              type: type
            }
            if (block.type === BlockType.IFRAME) {
              if (!src && !srcdoc) return
              block.iframeBlock = {
                src,
                srcdoc
              }
            } else if (block.type === BlockType.VIDEO) {
              if (!src) return
              block.videoBlock = {
                src
              }
            }
            const blockElement = {
              type: ElementType.BLOCK,
              value: '',
              // height(height),
              height,
              block
            }
            if (width) {
              blockElement.width = Number(width)
            }
            instance.command.executeInsertElementList([blockElement])
          }
        })
      }

      // 5. | 搜索&替换 | 打印 |
      const searchCollapseDom = document.querySelector(
        '.menu-item__search__collapse'
      )
      const searchInputDom = document.querySelector < HTMLInputElement > (
        '.menu-item__search__collapse__search input'
      )
      const replaceInputDom = document.querySelector < HTMLInputElement > (
        '.menu-item__search__collapse__replace input'
      )
      const searchDom =
        document.querySelector('.menu-item__search')
      searchDom.title = `搜索与替换(${isApple ? '⌘' : 'Ctrl'}+F)`
      const searchResultDom =
        searchCollapseDom.querySelector < HTMLLabelElement > ('.search-result')
      function setSearchResult() {
        const result = instance.command.getSearchNavigateInfo()
        if (result) {
          const { index, count } = result
          searchResultDom.innerText = `${index}/${count}`
        } else {
          searchResultDom.innerText = ''
        }
      }
      searchDom.onclick = function () {
        console.log('search')
        searchCollapseDom.style.display = 'block'
        const bodyRect = document.body.getBoundingClientRect()
        const searchRect = searchDom.getBoundingClientRect()
        const searchCollapseRect = searchCollapseDom.getBoundingClientRect()
        if (searchRect.left + searchCollapseRect.width > bodyRect.width) {
          searchCollapseDom.style.right = '0px'
          searchCollapseDom.style.left = 'unset'
        } else {
          searchCollapseDom.style.right = 'unset'
        }
        searchInputDom.focus()
      }
      searchCollapseDom.querySelector('span').onclick =
        function () {
          searchCollapseDom.style.display = 'none'
          searchInputDom.value = ''
          replaceInputDom.value = ''
          instance.command.executeSearch(null)
          setSearchResult()
        }
      searchInputDom.oninput = function () {
        instance.command.executeSearch(searchInputDom.value || null)
        setSearchResult()
      }
      searchInputDom.onkeydown = function (evt) {
        if (evt.key === 'Enter') {
          instance.command.executeSearch(searchInputDom.value || null)
          setSearchResult()
        }
      }
      searchCollapseDom.querySelector('button').onclick =
        function () {
          const searchValue = searchInputDom.value
          const replaceValue = replaceInputDom.value
          if (searchValue && replaceValue && searchValue !== replaceValue) {
            instance.command.executeReplace(replaceValue)
          }
        }
      searchCollapseDom.querySelector('.arrow-left').onclick =
        function () {
          instance.command.executeSearchNavigatePre()
          setSearchResult()
        }
      searchCollapseDom.querySelector('.arrow-right').onclick =
        function () {
          instance.command.executeSearchNavigateNext()
          setSearchResult()
        }

      const printDom = document.querySelector('.menu-item__print')
      printDom.title = `打印(${isApple ? '⌘' : 'Ctrl'}+P)`
      printDom.onclick = function () {
        console.log('print')
        instance.command.executePrint()
      }

      // 6. 目录显隐 | 页面模式 | 纸张缩放 | 纸张大小 | 纸张方向 | 页边距 | 全屏 | 设置
      const editorOptionDom =
        document.querySelector('.editor-option')
      editorOptionDom.onclick = function () {
        const options = instance.command.getOptions()
        new Dialog({
          title: '编辑器配置',
          data: [
            {
              type: 'textarea',
              name: 'option',
              width: 350,
              height: 300,
              required: true,
              value: JSON.stringify(options, null, 2),
              placeholder: '请输入编辑器配置'
            }
          ],
          onConfirm: payload => {
            const newOptionValue = payload.find(p => p.name === 'option')?.value
            if (!newOptionValue) return
            const newOption = JSON.parse(newOptionValue)
            instance.command.executeUpdateOptions(newOption)
          }
        })
      }

      async function updateCatalog() {
        const catalog = await instance.command.getCatalog()
        const catalogMainDom =
          document.querySelector('.catalog__main')
        catalogMainDom.innerHTML = ''
        if (catalog) {
          const appendCatalog = (
            parent,
            catalogItems
          ) => {
            for (let c = 0; c < catalogItems.length; c++) {
              const catalogItem = catalogItems[c]
              const catalogItemDom = document.createElement('div')
              catalogItemDom.classList.add('catalog-item')
              // 渲染
              const catalogItemContentDom = document.createElement('div')
              catalogItemContentDom.classList.add('catalog-item__content')
              const catalogItemContentSpanDom = document.createElement('span')
              catalogItemContentSpanDom.innerText = catalogItem.name
              catalogItemContentDom.append(catalogItemContentSpanDom)
              // 定位
              catalogItemContentDom.onclick = () => {
                instance.command.executeLocationCatalog(catalogItem.id)
              }
              catalogItemDom.append(catalogItemContentDom)
              if (catalogItem.subCatalog && catalogItem.subCatalog.length) {
                appendCatalog(catalogItemDom, catalogItem.subCatalog)
              }
              // 追加
              parent.append(catalogItemDom)
            }
          }
          appendCatalog(catalogMainDom, catalog)
        }
      }
      let isCatalogShow = true
      const catalogDom = document.querySelector < HTMLElement > ('.catalog')
      const catalogModeDom =
        document.querySelector('.catalog-mode')
      const catalogHeaderCloseDom = document.querySelector(
        '.catalog__header__close'
      )
      const switchCatalog = () => {
        isCatalogShow = !isCatalogShow
        if (isCatalogShow) {
          catalogDom.style.display = 'block'
          updateCatalog()
        } else {
          catalogDom.style.display = 'none'
        }
      }
      catalogModeDom.onclick = switchCatalog
      catalogHeaderCloseDom.onclick = switchCatalog

      const pageModeDom = document.querySelector('.page-mode')
      const pageModeOptionsDom =
        pageModeDom.querySelector('.options')
      pageModeDom.onclick = function () {
        pageModeOptionsDom.classList.toggle('visible')
      }
      pageModeOptionsDom.onclick = function (evt) {
        const li = evt.target
        instance.command.executePageMode(li.dataset.pageMode)
      }

      document.querySelector('.page-scale-percentage').onclick =
        function () {
          console.log('page-scale-recovery')
          instance.command.executePageScaleRecovery()
        }

      document.querySelector('.page-scale-minus').onclick =
        function () {
          console.log('page-scale-minus')
          instance.command.executePageScaleMinus()
        }

      document.querySelector('.page-scale-add').onclick =
        function () {
          console.log('page-scale-add')
          instance.command.executePageScaleAdd()
        }

      // 纸张大小
      const paperSizeDom = document.querySelector('.paper-size')
      const paperSizeDomOptionsDom =
        paperSizeDom.querySelector('.options')
      paperSizeDom.onclick = function () {
        paperSizeDomOptionsDom.classList.toggle('visible')
      }
      paperSizeDomOptionsDom.onclick = function (evt) {
        const li = evt.target
        const paperType = li.dataset.paperSize
        const [width, height] = paperType.split('*').map(Number)
        instance.command.executePaperSize(width, height)
        // 纸张状态回显
        paperSizeDomOptionsDom
          .querySelectorAll('li')
          .forEach(child => child.classList.remove('active'))
        li.classList.add('active')
      }

      // 纸张方向
      const paperDirectionDom =
        document.querySelector('.paper-direction')
      const paperDirectionDomOptionsDom =
        paperDirectionDom.querySelector('.options')
      paperDirectionDom.onclick = function () {
        paperDirectionDomOptionsDom.classList.toggle('visible')
      }
      paperDirectionDomOptionsDom.onclick = function (evt) {
        const li = evt.target
        const paperDirection = li.dataset.paperDirection
        instance.command.executePaperDirection(paperDirection)
        // 纸张方向状态回显
        paperDirectionDomOptionsDom
          .querySelectorAll('li')
          .forEach(child => child.classList.remove('active'))
        li.classList.add('active')
      }

      // 页面边距
      const paperMarginDom =
        document.querySelector('.paper-margin')
      paperMarginDom.onclick = function () {
        const [topMargin, rightMargin, bottomMargin, leftMargin] =
          instance.command.getPaperMargin()
        new Dialog({
          title: '页边距',
          data: [
            {
              type: 'text',
              label: '上边距',
              name: 'top',
              required: true,
              value: `${topMargin}`,
              placeholder: '请输入上边距'
            },
            {
              type: 'text',
              label: '下边距',
              name: 'bottom',
              required: true,
              value: `${bottomMargin}`,
              placeholder: '请输入下边距'
            },
            {
              type: 'text',
              label: '左边距',
              name: 'left',
              required: true,
              value: `${leftMargin}`,
              placeholder: '请输入左边距'
            },
            {
              type: 'text',
              label: '右边距',
              name: 'right',
              required: true,
              value: `${rightMargin}`,
              placeholder: '请输入右边距'
            }
          ],
          onConfirm: payload => {
            const top = payload.find(p => p.name === 'top')?.value
            if (!top) return
            const bottom = payload.find(p => p.name === 'bottom')?.value
            if (!bottom) return
            const left = payload.find(p => p.name === 'left')?.value
            if (!left) return
            const right = payload.find(p => p.name === 'right')?.value
            if (!right) return
            instance.command.executeSetPaperMargin([
              Number(top),
              Number(right),
              Number(bottom),
              Number(left)
            ])
          }
        })
      }

      // 全屏
      const fullscreenDom = document.querySelector('.fullscreen')
      fullscreenDom.onclick = toggleFullscreen
      window.addEventListener('keydown', evt => {
        if (evt.key === 'F11') {
          toggleFullscreen()
          evt.preventDefault()
        }
      })
      document.addEventListener('fullscreenchange', () => {
        fullscreenDom.classList.toggle('exist')
      })
      function toggleFullscreen() {
        console.log('fullscreen')
        if (!document.fullscreenElement) {
          document.documentElement.requestFullscreen()
        } else {
          document.exitFullscreen()
        }
      }

      // 7. 编辑器使用模式
      let modeIndex = 0
      const modeList = [
        {
          mode: EditorMode.EDIT,
          name: '编辑模式'
        },
        {
          mode: EditorMode.CLEAN,
          name: '清洁模式'
        },
        {
          mode: EditorMode.READONLY,
          name: '只读模式'
        },
        {
          mode: EditorMode.FORM,
          name: '表单模式'
        },
        {
          mode: EditorMode.PRINT,
          name: '打印模式'
        },
        {
          mode: EditorMode.DESIGN,
          name: '设计模式'
        }
      ]
      const modeElement = document.querySelector('.editor-mode')
      modeElement.onclick = function () {
        // 模式选择循环
        modeIndex === modeList.length - 1 ? (modeIndex = 0) : modeIndex++
        // 设置模式
        const { name, mode } = modeList[modeIndex]
        modeElement.innerText = name
        instance.command.executeMode(mode)
        // 设置菜单栏权限视觉反馈
        const isReadonly = mode === EditorMode.READONLY
        const enableMenuList = ['search', 'print']
        document.querySelectorAll('.menu-item>div').forEach(dom => {
          const menu = dom.dataset.menu
          isReadonly && (!menu || !enableMenuList.includes(menu))
            ? dom.classList.add('disable')
            : dom.classList.remove('disable')
        })
      }

      // 模拟批注
      const commentDom = document.querySelector('.comment')
      async function updateComment() {
        const groupIds = await instance.command.getGroupIds()
        for (const comment of commentList) {
          const activeCommentDom = commentDom.querySelector(
            `.comment-item[data-id='${comment.id}']`
          )
          // 编辑器是否存在对应成组id
          if (groupIds.includes(comment.id)) {
            // 当前dom是否存在-不存在则追加
            if (!activeCommentDom) {
              const commentItem = document.createElement('div')
              commentItem.classList.add('comment-item')
              commentItem.setAttribute('data-id', comment.id)
              commentItem.onclick = () => {
                instance.command.executeLocationGroup(comment.id)
              }
              commentDom.append(commentItem)
              // 选区信息
              const commentItemTitle = document.createElement('div')
              commentItemTitle.classList.add('comment-item__title')
              commentItemTitle.append(document.createElement('span'))
              const commentItemTitleContent = document.createElement('span')
              commentItemTitleContent.innerText = comment.rangeText
              commentItemTitle.append(commentItemTitleContent)
              const closeDom = document.createElement('i')
              closeDom.onclick = () => {
                instance.command.executeDeleteGroup(comment.id)
              }
              commentItemTitle.append(closeDom)
              commentItem.append(commentItemTitle)
              // 基础信息
              const commentItemInfo = document.createElement('div')
              commentItemInfo.classList.add('comment-item__info')
              const commentItemInfoName = document.createElement('span')
              commentItemInfoName.innerText = comment.userName
              const commentItemInfoDate = document.createElement('span')
              commentItemInfoDate.innerText = comment.createdDate
              commentItemInfo.append(commentItemInfoName)
              commentItemInfo.append(commentItemInfoDate)
              commentItem.append(commentItemInfo)
              // 详细评论
              const commentItemContent = document.createElement('div')
              commentItemContent.classList.add('comment-item__content')
              commentItemContent.innerText = comment.content
              commentItem.append(commentItemContent)
              commentDom.append(commentItem)
            }
          } else {
            // 编辑器内不存在对应成组id则dom则移除
            activeCommentDom?.remove()
          }
        }
      }
      // 8. 内部事件监听
      instance.listener.rangeStyleChange = function (payload) {
        // 控件类型
        payload.type === ElementType.SUBSCRIPT
          ? subscriptDom.classList.add('active')
          : subscriptDom.classList.remove('active')
        payload.type === ElementType.SUPERSCRIPT
          ? superscriptDom.classList.add('active')
          : superscriptDom.classList.remove('active')
        payload.type === ElementType.SEPARATOR
          ? separatorDom.classList.add('active')
          : separatorDom.classList.remove('active')
        separatorOptionDom
          .querySelectorAll('li')
          .forEach(li => li.classList.remove('active'))
        if (payload.type === ElementType.SEPARATOR) {
          const separator = payload.dashArray.join(',') || '0,0'
          const curSeparatorDom = separatorOptionDom.querySelector < HTMLLIElement > (
            `[data-separator='${separator}']`
          )
          if (curSeparatorDom) {
            curSeparatorDom.classList.add('active')
          }
        }

        // 富文本
        fontOptionDom
          .querySelectorAll < HTMLLIElement > ('li')
            .forEach(li => li.classList.remove('active'))
        const curFontDom = fontOptionDom.querySelector < HTMLLIElement > (
          `[data-family='${payload.font}']`
        )
        if (curFontDom) {
          fontSelectDom.innerText = curFontDom.innerText
          fontSelectDom.style.fontFamily = payload.font
          curFontDom.classList.add('active')
        }
        sizeOptionDom
          .querySelectorAll < HTMLLIElement > ('li')
            .forEach(li => li.classList.remove('active'))
        const curSizeDom = sizeOptionDom.querySelector < HTMLLIElement > (
          `[data-size='${payload.size}']`
        )
        if (curSizeDom) {
          sizeSelectDom.innerText = curSizeDom.innerText
          curSizeDom.classList.add('active')
        } else {
          sizeSelectDom.innerText = `${payload.size}`
        }
        payload.bold
          ? boldDom.classList.add('active')
          : boldDom.classList.remove('active')
        payload.italic
          ? italicDom.classList.add('active')
          : italicDom.classList.remove('active')
        payload.underline
          ? underlineDom.classList.add('active')
          : underlineDom.classList.remove('active')
        payload.strikeout
          ? strikeoutDom.classList.add('active')
          : strikeoutDom.classList.remove('active')
        if (payload.color) {
          colorDom.classList.add('active')
          colorControlDom.value = payload.color
          colorSpanDom.style.backgroundColor = payload.color
        } else {
          colorDom.classList.remove('active')
          colorControlDom.value = '#000000'
          colorSpanDom.style.backgroundColor = '#000000'
        }
        if (payload.highlight) {
          highlightDom.classList.add('active')
          highlightControlDom.value = payload.highlight
          highlightSpanDom.style.backgroundColor = payload.highlight
        } else {
          highlightDom.classList.remove('active')
          highlightControlDom.value = '#ffff00'
          highlightSpanDom.style.backgroundColor = '#ffff00'
        }

        // 行布局
        leftDom.classList.remove('active')
        centerDom.classList.remove('active')
        rightDom.classList.remove('active')
        alignmentDom.classList.remove('active')
        justifyDom.classList.remove('active')
        if (payload.rowFlex && payload.rowFlex === 'right') {
          rightDom.classList.add('active')
        } else if (payload.rowFlex && payload.rowFlex === 'center') {
          centerDom.classList.add('active')
        } else if (payload.rowFlex && payload.rowFlex === 'alignment') {
          alignmentDom.classList.add('active')
        } else if (payload.rowFlex && payload.rowFlex === 'justify') {
          justifyDom.classList.add('active')
        } else {
          leftDom.classList.add('active')
        }

        // 行间距
        rowOptionDom
          .querySelectorAll < HTMLLIElement > ('li')
            .forEach(li => li.classList.remove('active'))
        const curRowMarginDom = rowOptionDom.querySelector < HTMLLIElement > (
          `[data-rowmargin='${payload.rowMargin}']`
        )
        curRowMarginDom.classList.add('active')

        // 功能
        payload.undo
          ? undoDom.classList.remove('no-allow')
          : undoDom.classList.add('no-allow')
        payload.redo
          ? redoDom.classList.remove('no-allow')
          : redoDom.classList.add('no-allow')
        payload.painter
          ? painterDom.classList.add('active')
          : painterDom.classList.remove('active')

        // 标题
        titleOptionDom
          .querySelectorAll < HTMLLIElement > ('li')
            .forEach(li => li.classList.remove('active'))
        if (payload.level) {
          const curTitleDom = titleOptionDom.querySelector < HTMLLIElement > (
            `[data-level='${payload.level}']`
          )
          titleSelectDom.innerText = curTitleDom.innerText
          curTitleDom.classList.add('active')
        } else {
          titleSelectDom.innerText = '正文'
          titleOptionDom.querySelector('li:first-child').classList.add('active')
        }

        // 列表
        listOptionDom
          .querySelectorAll < HTMLLIElement > ('li')
            .forEach(li => li.classList.remove('active'))
        if (payload.listType) {
          listDom.classList.add('active')
          const listType = payload.listType
          const listStyle =
            payload.listType === ListType.OL ? ListStyle.DECIMAL : payload.listType
          const curListDom = listOptionDom.querySelector < HTMLLIElement > (
            `[data-list-type='${listType}'][data-list-style='${listStyle}']`
          )
          if (curListDom) {
            curListDom.classList.add('active')
          }
        } else {
          listDom.classList.remove('active')
        }

        // 批注
        commentDom
          .querySelectorAll('.comment-item')
          .forEach(commentItemDom => {
            commentItemDom.classList.remove('active')
          })
        if (payload.groupIds) {
          const [id] = payload.groupIds
          const activeCommentDom = commentDom.querySelector(
            `.comment-item[data-id='${id}']`
          )
          if (activeCommentDom) {
            activeCommentDom.classList.add('active')
            scrollIntoView(commentDom, activeCommentDom)
          }
        }
      }

      instance.listener.visiblePageNoListChange = function (payload) {
        const text = payload.map(i => i + 1).join('、')
        document.querySelector('.page-no-list').innerText = text
      }

      instance.listener.pageSizeChange = function (payload) {
        document.querySelector(
          '.page-size'
        ).innerText = `${payload}`
      }

      instance.listener.intersectionPageNoChange = function (payload) {
        document.querySelector('.page-no').innerText = `${payload + 1
          }`
      }

      instance.listener.pageScaleChange = function (payload) {
        document.querySelector(
          '.page-scale-percentage'
        ).innerText = `${Math.floor(payload * 10 * 10)}%`
      }

      instance.listener.controlChange = function (payload) {
        const disableMenusInControlContext = [
          'table',
          'hyperlink',
          'separator',
          'page-break',
          'control'
        ]
        // 菜单操作权限
        disableMenusInControlContext.forEach(menu => {
          const menuDom = document.querySelector(
            `.menu-item__${menu}`
          )
          payload
            ? menuDom.classList.add('disable')
            : menuDom.classList.remove('disable')
        })
      }

      instance.listener.pageModeChange = function (payload) {
        const activeMode = pageModeOptionsDom.querySelector(
          `[data-page-mode='${payload}']`
        )
        pageModeOptionsDom
          .querySelectorAll('li')
          .forEach(li => li.classList.remove('active'))
        activeMode.classList.add('active')
      }

      const handleContentChange = async function () {
        // 字数
        const wordCount = await instance.command.getWordCount()
        document.querySelector('.word-count').innerText = `${wordCount || 0
          }`
        // 目录
        if (isCatalogShow) {
          nextTick(() => {
            updateCatalog()
          })
        }
        // 批注
        nextTick(() => {
          updateComment()
        })
      }
      instance.listener.contentChange = debounce(handleContentChange, 200)
      handleContentChange()

      instance.listener.saved = function (payload) {
        console.log('elementList: ', payload)
      }

      // 9. 右键菜单注册
      instance.register.contextMenuList([
        {
          name: '批注',
          when: payload => {
            return (
              !payload.isReadonly &&
              payload.editorHasSelection &&
              payload.zone === EditorZone.MAIN
            )
          },
          callback: (command) => {
            new Dialog({
              title: '批注',
              data: [
                {
                  type: 'textarea',
                  label: '批注',
                  height: 100,
                  name: 'value',
                  required: true,
                  placeholder: '请输入批注'
                }
              ],
              onConfirm: payload => {
                const value = payload.find(p => p.name === 'value')?.value
                if (!value) return
                const groupId = command.executeSetGroup()
                if (!groupId) return
                commentList.push({
                  id: groupId,
                  content: value,
                  userName: 'Hufe',
                  rangeText: rangeText.getRangeText(),
                  createdDate: new Date().toLocaleString()
                })
              }
            })
          }
        },
        {
          name: '签名',
          icon: 'signature',
          when: payload => {
            return !payload.isReadonly && payload.editorTextFocus
          },
          callback: (command) => {
            new Signature({
              onConfirm(payload) {
                if (!payload) return
                const { value, width, height } = payload
                if (!value || !width || !height) return
                command.executeInsertElementList([
                  {
                    value,
                    width,
                    height,
                    type: ElementType.IMAGE
                  }
                ])
              }
            })
          }
        },
        {
          name: '格式整理',
          icon: 'word-tool',
          when: payload => {
            return !payload.isReadonly
          },
          callback: (command) => {
            command.executeWordTool()
          }
        }
      ])

      // 10. 快捷键注册
      instance.register.shortcutList([
        {
          key: KeyMap.P,
          mod: true,
          isGlobal: true,
          callback: (command) => {
            command.executePrint()
          }
        },
        {
          key: KeyMap.F,
          mod: true,
          isGlobal: true,
          callback: (command) => {
            const text = command.getRangeText()
            searchDom.click()
            if (text) {
              searchInputDom.value = text
              instance.command.executeSearch(text)
              setSearchResult()
            }
          }
        },
        {
          key: KeyMap.MINUS,
          ctrl: true,
          isGlobal: true,
          callback: (command) => {
            command.executePageScaleMinus()
          }
        },
        {
          key: KeyMap.EQUAL,
          ctrl: true,
          isGlobal: true,
          callback: (command) => {
            command.executePageScaleAdd()
          }
        },
        {
          key: KeyMap.ZERO,
          ctrl: true,
          isGlobal: true,
          callback: (command) => {
            command.executePageScaleRecovery()
          }
        }
      ])
    },
  }
}
</script>
<style scoped>
.zjm-editor {
  position: relative;
  padding: 80px 0 30px;
}
</style>
